import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map.Entry;

// Class that manages the creation and the draw of a bar chart
public class BarChart extends GuiElement{

	public int TYPE; // 0 hours chart, 1 lastfm top artists, 2 db top artists, 3 saved chart 
	public float plotHeight;

	public GuiElement rolloverRect;

	public Object[] valueMaps;
	
	// Graph height variables
	
	int maxYValue;
	float  verticalMinValue;
	int verticalMinInterval,verticalMaxInterval;


	public BarChart(float x, float y, float w, float h, int type) {	
		this.x = x;
		this.y = y;
		width = w;
		height = h;
		plotHeight = h - 40;
		TYPE = type;

		// Rollover rect
		rolloverRect = new GuiElement();

		// Init
		GLOBAL.lastfm_index_start = 0;
		
		if(type == 1){
			maxYValue=2000000;
			verticalMinValue=0;
			verticalMinInterval=2000000/20;
			verticalMaxInterval=2000000/5;
		}
		else if (type == 2 || type ==3) {
			maxYValue=60;
			verticalMinValue=0;
			verticalMinInterval=60/20;
			verticalMaxInterval=60/5;
		}


	}

	public void draw() {

		GLOBAL.processing.strokeWeight(1);
		GLOBAL.processing.stroke(GLOBAL.colorLines);
		GLOBAL.processing.fill(GLOBAL.colorChartBackground);
		GLOBAL.processing.rectMode(GLOBAL.processing.CORNER);
		GLOBAL.processing.rect(x, y, width, plotHeight);
		
		if (TYPE == 0)
			createDayChart();
		if (TYPE == 1)
			createLastfmTopArtistsChart();
		if (TYPE == 2)
			createDbTopArtistsChart();
		if (TYPE == 3)
			createSavedTopArtistChart();

		GLOBAL.processing.strokeWeight(1);
		GLOBAL.processing.stroke(GLOBAL.colorLines);
		GLOBAL.processing.line(x, y+plotHeight, x + width, y+plotHeight);


	}


	// Create a chart of day type
	public void createDayChart(){

		float[] values = new float[24];
		float value = 0;

		// Initialization
		for (int i=0; i< values.length; i++)
			values[i] = 0;

		float barY = y + plotHeight;
		float rectWidth = (float)(width)/(2*25);

		drawVerticalLabels(0, 100, 5, 10, " K");
		drawUnits("# / HH");

		for (Entry<Nationality,ArrayList<Float> > entry : GLOBAL.map_nPerHourPerNationality.entrySet()) {

			// Values of this nationality
			ArrayList<Float> valuePerHour = entry.getValue();

			// Update hours counts
			for (int i=0; i<valuePerHour.size(); i++)
				if(valuePerHour.get(i) > 0)
					values[i] = values[i] + valuePerHour.get(i).intValue();
		}


		GLOBAL.COLORS.reset();

		// For each hour
		for(int i =0; i < values.length; i++) {

			// Draw the rect
			barY = GLOBAL.processing.map(values[i], 0, 100000, y + plotHeight, y);
			float barX = GLOBAL.processing.map(i, 0, values.length-1, x + rectWidth, x + width - rectWidth);
			GLOBAL.processing.noStroke();
			GLOBAL.processing.rectMode(GLOBAL.processing.CORNERS);
			GLOBAL.processing.fill(GLOBAL.COLORS.getNextColor());
			GLOBAL.processing.rect( barX - rectWidth/2, barY, barX+ rectWidth/2, y + plotHeight);

			// Draw the volume name
			GLOBAL.processing.fill(GLOBAL.textColor);
			GLOBAL.processing.textFont(GLOBAL.plotFont,10);
			GLOBAL.processing.textAlign(GLOBAL.processing.CENTER);
			GLOBAL.processing.text(i, barX , y + plotHeight + 15);

		}

		// Check for any mouse rollover functionality to be displayed
		for(int i =0; i < values.length; i++) {
			value = values[i];
			float barX = GLOBAL.processing.map(i, 0, values.length-1, x + rectWidth, x + width - rectWidth);
			if (value > 0 && GLOBAL.processing.mouseX > (barX - rectWidth/2) && GLOBAL.processing.mouseX < (barX + rectWidth) 
					&& GLOBAL.processing.mouseY > y && GLOBAL.processing.mouseY < (y  + plotHeight)) 
				rollover(barX, value, "", rectWidth);
		}
	}

	public void createLastfmTopArtistsChart() {
		float value = 0;

		float barY = y + plotHeight;
		float rectWidth = (float)(width)/(2*(10));

		//drawVerticalLabels(0, 2000, 100, 500, " K");
		drawVerticalLabels(0, maxYValue/1000, verticalMinInterval/1000, verticalMaxInterval/1000, " K");

		//drawUnits("# / HH");
		
		drawTitle("LastFM top artists now  -  Total playcount per artist");

		GLOBAL.COLORS.reset();
		float maxValue = 0;
		int color = GLOBAL.COLORS.getColor(0);

		try{
		
		// For each artist
		for(int i = GLOBAL.lastfm_index_start; i < GLOBAL.lastfm_index_start+10; i++) {

			int cy = 0;

			if(GLOBAL.lastfm_TopArtistsPlayCount.size()==0)
				return;

			if (i%2 == 0 && GLOBAL.lastfm_index_start%2 == 0)
				cy = 20;
			else if (i%2 == 0 && GLOBAL.lastfm_index_start%2 == 1)
				cy = 20;

			// Draw the rect
			barY = GLOBAL.processing.map(GLOBAL.lastfm_TopArtistsPlayCount.get(i), 0, maxYValue, y + plotHeight, y);
			if(maxValue<GLOBAL.lastfm_TopArtistsPlayCount.get(i))maxValue = GLOBAL.lastfm_TopArtistsPlayCount.get(i);
			float barX = GLOBAL.processing.map(i, GLOBAL.lastfm_index_start, GLOBAL.lastfm_index_start+9, x + rectWidth, x + width - rectWidth);
			GLOBAL.processing.noStroke();
			GLOBAL.processing.rectMode(GLOBAL.processing.CORNERS);
			GLOBAL.processing.fill(color);
			GLOBAL.processing.rect( barX - rectWidth/2, barY, barX+ rectWidth/2, y + plotHeight);

			// Draw the volume name
			GLOBAL.processing.fill(GLOBAL.textColor);
			GLOBAL.processing.textFont(GLOBAL.plotFont,10);
			GLOBAL.processing.textAlign(GLOBAL.processing.CENTER);
			GLOBAL.processing.text((i+1)+"-"+GLOBAL.lastfm_TopArtistsNames.get(i), barX , cy + y + plotHeight + 15);

		}

		// Check for any mouse rollover functionality to be displayed
		for(int i = GLOBAL.lastfm_index_start; i <= GLOBAL.lastfm_index_start+9; i++) {
			value = GLOBAL.lastfm_TopArtistsPlayCount.get(i);
			float barX = GLOBAL.processing.map(i, GLOBAL.lastfm_index_start, GLOBAL.lastfm_index_start+9, x + rectWidth, x + width - rectWidth);
			if (value > 0 && GLOBAL.processing.mouseX > (barX - rectWidth/2) && GLOBAL.processing.mouseX < (barX + rectWidth) 
					&& GLOBAL.processing.mouseY > y && GLOBAL.processing.mouseY < (y  + plotHeight)) 
				rollover(barX, (int)((float)value/1000+0.5), " K", rectWidth);
		}

		checkMaxYVal(maxValue);
		}catch (Exception e){
		}
	}

	private void checkMaxYVal(float maxValue) {

		int threshold = maxYValue/10;

		if(maxValue > maxYValue - maxYValue/10){
			if(TYPE==2 || TYPE ==3)
				maxYValue = maxYValue +60;
			else if(TYPE ==1)
				maxYValue = maxYValue + 500000;
			verticalMaxInterval = maxYValue/5;
			verticalMinInterval = maxYValue/20;
		}
	}

	public void createDbTopArtistsChart() {
		float value = 0;
		float barY = y + plotHeight;

		drawVerticalLabels(0, maxYValue, verticalMinInterval, verticalMaxInterval, "");
		
		drawTitle("Search result  -  Artist's playcount over selected users ( # "+GLOBAL.totalUsersResult+" )");

		//drawUnits("# / HH");
		float maxValue = 0;


		if (GLOBAL.nationalitiesSelected.size() == 0) {
			valueMaps = null;
			return;
		}

		if (GLOBAL.charts_nPlaysPerArtist_sorted.size() == 0) {
			valueMaps = null;
			return;
		}
		if (GLOBAL.charts_nPlaysPerArtist_sorted == null) {
			valueMaps = null;
			return;
		}

		try {


			if (GLOBAL.chartsArea.thread.isAlive()) {
				valueMaps = GLOBAL.charts_nPlaysPerArtist_sorted.values().toArray();
				Arrays.sort(valueMaps);
			}
			else if (!GLOBAL.chartsArea.thread_gotUpdatedResults && GLOBAL.chartsArea.thread_finished) {
				GLOBAL.chartsArea.thread_gotUpdatedResults = true;
				valueMaps = GLOBAL.charts_nPlaysPerArtist_sorted.values().toArray();
				Arrays.sort(valueMaps);
			}

			float rectWidth = (float)(width)/(2*10);

			GLOBAL.COLORS.reset();
			int color = GLOBAL.COLORS.getColor(1);
			int index_max = 10;

			if (valueMaps == null) return;

			if (valueMaps.length < index_max)
				index_max = valueMaps.length;

			for (int i=GLOBAL.topArtists_index_start; i< GLOBAL.topArtists_index_start+index_max; i++) {

				//if (valueMaps[i] == null ) break;

				// Values of this nationality
				ValueMap vm = (ValueMap) valueMaps[i];

				int cy = 0;

				if (i%2 == 0 && GLOBAL.topArtists_index_start%2 == 0)
					cy = 20;
				else if (i%2 == 0 && GLOBAL.topArtists_index_start%2 == 1)
					cy = 20;

				// Draw the rect
				barY = GLOBAL.processing.map(vm.value, 0,maxYValue, y + plotHeight, y);
				if(maxValue<vm.value)maxValue = vm.value;
				float barX = GLOBAL.processing.map(i, GLOBAL.topArtists_index_start, GLOBAL.topArtists_index_start+index_max-1, x + rectWidth, x + width - rectWidth);
				GLOBAL.processing.noStroke();
				GLOBAL.processing.rectMode(GLOBAL.processing.CORNERS);
				GLOBAL.processing.fill(color);
				GLOBAL.processing.rect( barX - rectWidth/2, barY, barX+ rectWidth/2, y + plotHeight);

				// Draw the volume name
				GLOBAL.processing.fill(GLOBAL.textColor);
				GLOBAL.processing.textFont(GLOBAL.plotFont,10);
				GLOBAL.processing.textAlign(GLOBAL.processing.CENTER);
				GLOBAL.processing.text((i+1)+"-"+vm.name, barX , cy + y + plotHeight + 15);

			}

			// Check for any mouse rollover functionality to be displayed
			for(int i = GLOBAL.topArtists_index_start; i <= GLOBAL.topArtists_index_start+index_max-1; i++) {
				if (valueMaps[i] == null ) break;
				value = ((ValueMap)valueMaps[i]).value;
				float barX = GLOBAL.processing.map(i, GLOBAL.topArtists_index_start, GLOBAL.topArtists_index_start+9, x + rectWidth, x + width - rectWidth);
				if (value > 0 && GLOBAL.processing.mouseX > (barX - rectWidth/2) && GLOBAL.processing.mouseX < (barX + rectWidth) 
						&& GLOBAL.processing.mouseY > y && GLOBAL.processing.mouseY < (y  + plotHeight)) 
					rollover(barX, value, "", rectWidth);
			}

		}
		catch (Exception e) {
			System.err.println("Bar chart creation error");
			//e.printStackTrace();
		}
		
		// Check if the other chart has a greater scale
		if(GLOBAL.chartsArea.saved_chart!=null){
			if(GLOBAL.chartsArea.saved_chart.maxYValue > maxYValue)
				maxValue = GLOBAL.chartsArea.saved_chart.maxYValue;
		}

		checkMaxYVal(maxValue);


	}
	
	public void createSavedTopArtistChart() {
		
		float value = 0;
		float barY = y + plotHeight;

		//drawVerticalLabels(0, 100, 5, 10, " K");
		drawVerticalLabels(0, maxYValue, verticalMinInterval, verticalMaxInterval, "");
		
		drawTitle("Saved result  -  Artist's playcount over selected users ( # "+GLOBAL.totalUsersSaved+" )");

		//drawUnits("# / HH");
		float maxValue = 0;


		try{
		if (GLOBAL.charts_nPlaysPerArtist_sortedSaved.length == 0 
				|| GLOBAL.charts_nPlaysPerArtist_sortedSaved == null) {
			return;
		}

		float rectWidth = (float)(width)/(2*10);

		GLOBAL.COLORS.reset();
		int color = GLOBAL.COLORS.getColor(2);

		int index_max = 10;

		if (GLOBAL.charts_nPlaysPerArtist_sortedSaved.length < index_max)
			index_max = valueMaps.length;

		for (int i=GLOBAL.savedChart_index_start; i< GLOBAL.savedChart_index_start+index_max; i++) {

			// Values of this nationality
			ValueMap vm = (ValueMap)GLOBAL.charts_nPlaysPerArtist_sortedSaved[i];

			int cy = 0;

			if (i%2 == 0 && GLOBAL.savedChart_index_start%2 == 0)
				cy = 20;
			else if (i%2 == 0 && GLOBAL.savedChart_index_start%2 == 1)
				cy = 20;

			// Draw the rect
			barY = GLOBAL.processing.map(vm.value, 0,maxYValue, y + plotHeight, y);
			if(maxValue<vm.value)maxValue = vm.value;
			float barX = GLOBAL.processing.map(i, GLOBAL.savedChart_index_start, GLOBAL.savedChart_index_start+index_max-1, x + rectWidth, x + width - rectWidth);
			GLOBAL.processing.noStroke();
			GLOBAL.processing.rectMode(GLOBAL.processing.CORNERS);
			GLOBAL.processing.fill(color);
			GLOBAL.processing.rect( barX - rectWidth/2, barY, barX+ rectWidth/2, y + plotHeight);

			// Draw the volume name
			GLOBAL.processing.fill(GLOBAL.textColor);
			GLOBAL.processing.textFont(GLOBAL.plotFont,10);
			GLOBAL.processing.textAlign(GLOBAL.processing.CENTER);
			GLOBAL.processing.text((i+1)+"-"+vm.name, barX , cy + y + plotHeight + 15);

		}

		// Check for any mouse rollover functionality to be displayed
		for(int i = GLOBAL.savedChart_index_start; i <= GLOBAL.savedChart_index_start+index_max-1; i++) {
			if (GLOBAL.charts_nPlaysPerArtist_sortedSaved[i] == null ) break;
			value = ((ValueMap)GLOBAL.charts_nPlaysPerArtist_sortedSaved[i]).value;
			float barX = GLOBAL.processing.map(i, GLOBAL.savedChart_index_start, GLOBAL.savedChart_index_start+9, x + rectWidth, x + width - rectWidth);
			if (value > 0 && GLOBAL.processing.mouseX > (barX - rectWidth/2) && GLOBAL.processing.mouseX < (barX + rectWidth) 
					&& GLOBAL.processing.mouseY > y && GLOBAL.processing.mouseY < (y  + plotHeight)) 
				rollover(barX, value, "", rectWidth);
		}

		// Check if the other chart has a greater scale
		if(GLOBAL.chartsArea.topArtist_chart!=null){
			if(GLOBAL.chartsArea.topArtist_chart.maxYValue > maxYValue)
				maxValue = GLOBAL.chartsArea.topArtist_chart.maxYValue;
		}
		checkMaxYVal(maxValue);
		}catch(Exception e){
			
		}

	}
	
	public void changePosition(float xValue, float yValue, float w, float l) {
		
		x = xValue;
		y = yValue;
		
		width = w;
		height = l;
		
		plotHeight = height - 40;
		
	}

	public void rollover(float x, float value, String label, float RectWidth) {

		rolloverRect.x = (int)(x - RectWidth/2);
		rolloverRect.y = (int)y;
		rolloverRect.width = (int)(RectWidth);
		rolloverRect.height = (int)plotHeight;

		rolloverRect.draw();
		
		int x1 = GLOBAL.processing.mouseX;

		if(TYPE== 1) {
			
			if (GLOBAL.processing.mouseX+180 > GLOBAL.width) {
				x1 = GLOBAL.processing.mouseX - 120;
			}
			
			// Rectangle
			GLOBAL.processing.stroke(GLOBAL.colorLines);
			GLOBAL.processing.rectMode(GLOBAL.processing.CORNER);
			GLOBAL.processing.fill(GLOBAL.colorBackground);
			GLOBAL.processing.rect(x1 + 10, GLOBAL.processing.mouseY - 28 , 100, 20); //x,y,width,height

			// Text
			GLOBAL.processing.fill(GLOBAL.textColor);
			GLOBAL.processing.textFont(GLOBAL.plotFont,14);
			GLOBAL.processing.textAlign(GLOBAL.processing.CENTER);
			GLOBAL.processing.text((int)(value+0.5)+label+" plays", x1 + 60, GLOBAL.processing.mouseY -12);
		}
		else {
			
			if (GLOBAL.processing.mouseX+180 > GLOBAL.width) {
				x1 = GLOBAL.processing.mouseX - 190;
			}
			
			// Rectangle
			GLOBAL.processing.stroke(GLOBAL.colorLines);
			GLOBAL.processing.rectMode(GLOBAL.processing.CORNER);
			GLOBAL.processing.fill(GLOBAL.colorBackground);
			GLOBAL.processing.rect(x1 + 10, GLOBAL.processing.mouseY - 28 , 180, 20); //x,y,width,height

			// Text
			GLOBAL.processing.fill(GLOBAL.textColor);
			GLOBAL.processing.textFont(GLOBAL.plotFont,14);
			GLOBAL.processing.textAlign(GLOBAL.processing.CENTER);
			GLOBAL.processing.text((int)(value+0.5)+" plays per user", x1 + 100, GLOBAL.processing.mouseY -12);
		}

	}

	// Draw units of the chart
	public void drawUnits(String u) {
		// Text
		GLOBAL.processing.fill(GLOBAL.textColor);
		GLOBAL.processing.textFont(GLOBAL.plotFont,9);
		GLOBAL.processing.textAlign(GLOBAL.processing.CENTER);
		GLOBAL.processing.text(u, x - 13, y + plotHeight + 23);
	}

	// Draw vertical labels of the chart
	public void drawVerticalLabels(float minValue, float maxValue, int minInterval, int maxInterval, String s){
		GLOBAL.processing.fill(GLOBAL.textColor);
		GLOBAL.processing.textFont(GLOBAL.plotFont,10);
		GLOBAL.processing.stroke(GLOBAL.colorLines);
		GLOBAL.processing.strokeWeight(1);

		float min = minValue;
		float max = maxValue;

		int yIntervalMinor = minInterval;
		int yIntervalMayor = maxInterval;

		float plotX1 = x;
		float plotX2 = x + width;

		for (float v = min; v <= max; v += yIntervalMinor) {

			if (v % yIntervalMinor == 0) {     // If a tick mark
				float y = GLOBAL.processing.map(v, min, max, this.y + plotHeight, this.y);  
				if (v % yIntervalMayor == 0) {        // If a major tick mark
					float textOffset = GLOBAL.processing.textAscent()/3;  // Center vertically
					if (v == min) {
						textOffset = 0;                   // Align by the bottom
					} 
					else if (v == max) {
						textOffset = GLOBAL.processing.textAscent() - 4;        // Align by the top
					}
					GLOBAL.processing.textAlign(GLOBAL.processing.RIGHT);
					GLOBAL.processing.text(GLOBAL.processing.floor(v) + s, plotX1 - 8, y + textOffset);
					if (v % maxInterval == 0) {
						GLOBAL.processing.line(plotX1 - 4, y, plotX2, y);
					}
					else {
						GLOBAL.processing.line(plotX1 - 4, y, plotX1, y);     // Draw major tick
					}
				} 
				else {
					GLOBAL.processing.line(plotX1 - 2, y, plotX1, y);   // Draw minor tick
				}
			}
		}

		GLOBAL.processing.noStroke();
	}
	
	public void drawTitle(String label) {
		
		GLOBAL.processing.textFont(GLOBAL.plotFont, 16);
		GLOBAL.processing.fill(GLOBAL.textColor);
		GLOBAL.processing.textAlign(GLOBAL.processing.LEFT);
		
		GLOBAL.processing.text(label, x,y-20);
		
	}

}
